/**
 * HTTP文件传输客户端
 * 支持断点续传和分片上传/下载
 */
const axios = require('axios');
const fs = require('fs');
const fsPromises = require('fs').promises;
const path = require('path');
const FormData = require('form-data');
const logger = require('./logger');
const config = require('./config');
const EventEmitter = require('events');
const { uploadLimiter, downloadLimiter } = require('./rateLimiter'); // 🔥 流量限制器

class HTTPClient extends EventEmitter {
  constructor() {
    super();
    this.chunkSize = 1024 * 1024 * 5; // 5MB 分片大小
  }

  /**
   * 处理401错误，触发登出
   */
  handleUnauthorized() {
    logger.error('检测到401未授权错误，触发自动登出');
    this.emit('unauthorized');
  }

  /**
   * 获取axios实例（带认证）
   */
  getAxiosInstance() {
    const userConfig = config.getUserConfig();
    const serverUrl = config.getServerUrl();
    
    if (!userConfig.token) {
      logger.error('未找到JWT令牌，用户可能未登录');
      throw new Error('未登录，请先登录');
    }
    
    logger.info(`创建HTTP客户端，服务器: ${serverUrl}`);
    
    return axios.create({
      baseURL: serverUrl,
      headers: {
        'Authorization': `Bearer ${userConfig.token}`,
        'Connection': 'keep-alive'
      },
      timeout: 0, // 无超时限制（大文件上传需要）
      maxContentLength: Infinity,
      maxBodyLength: Infinity,
      validateStatus: function (status) {
        return status < 500; // 只有5xx错误才会reject
      },
      // 添加重试配置
      httpAgent: new (require('http').Agent)({
        keepAlive: true,
        keepAliveMsecs: 60000,
        maxSockets: 10,
        maxFreeSockets: 5,
        timeout: 0
      }),
      httpsAgent: new (require('https').Agent)({
        keepAlive: true,
        keepAliveMsecs: 60000,
        maxSockets: 10,
        maxFreeSockets: 5,
        timeout: 0
      })
    });
  }

  /**
   * 列出远程文件
   */
  async list(dirPath = '/') {
    try {
      const axiosInstance = this.getAxiosInstance();
      logger.info(`正在获取远程文件列表: ${dirPath}`);
      
      const response = await axiosInstance.get('/api/files', {
        params: { path: dirPath }
      });

      logger.info(`HTTP响应状态: ${response.status}`);
      
      if (response.status === 401) {
        this.handleUnauthorized();
        throw new Error('认证失败，请重新登录');
      }
      
      if (response.status === 404) {
        throw new Error('API端点不存在，请检查服务器配置');
      }
      
      if (!response.data.success) {
        const errorMsg = response.data.message || '获取文件列表失败';
        logger.error(`服务器错误: ${errorMsg}`);
        throw new Error(errorMsg);
      }

      const files = response.data.data.files || [];
      logger.info(`成功获取 ${files.length} 个文件/文件夹`);
      return files;
    } catch (error) {
      logger.error('列出文件失败:', error.message);
      if (error.response) {
        logger.error(`HTTP状态: ${error.response.status}`, error.response.data);
      }
      throw error;
    }
  }

  /**
   * 上传文件（支持断点续传）
   */
  async uploadFile(localPath, remotePath, onProgress) {
    try {
      const stats = fs.statSync(localPath);
      const totalSize = stats.size;
      const fileName = path.basename(localPath);

      logger.info(`开始上传文件: ${fileName}, 大小: ${totalSize}`);

      // 使用快速哈希代替完整MD5，大幅提升大文件上传速度
      // 快速哈希：只读取文件头尾各1MB + 文件元数据
      let fileHash;
      
      // 对于超大文件（>100MB），跳过秒传检查，直接上传以节省时间
      const SKIP_CHECK_SIZE = 100 * 1024 * 1024; // 100MB
      const skipQuickCheck = totalSize > SKIP_CHECK_SIZE;
      
      if (!skipQuickCheck) {
        fileHash = await this.calculateFastHash(localPath);
        
        // 检查文件是否已存在（秒传功能）
        const exists = await this.checkFileExists(fileHash, fileName);

        if (exists) {
          logger.info(`文件已存在，跳过上传（秒传）: ${fileName}`);
          if (onProgress) {
            onProgress({ transferred: totalSize, total: totalSize, speed: 0 });
          }
          return { skipped: true, reason: 'exists' };
        }
      } else {
        // 超大文件：生成简单哈希用于分片标识
        fileHash = await this.calculateFastHash(localPath);
        logger.info(`文件较大（${(totalSize / 1024 / 1024).toFixed(2)}MB），跳过秒传检查，直接上传`);
      }

      // 小文件直接上传
      if (totalSize < this.chunkSize) {
        return await this.uploadSmallFile(localPath, remotePath, onProgress);
      }

      // 大文件分片上传
      return await this.uploadLargeFile(localPath, remotePath, fileHash, onProgress);
    } catch (error) {
      logger.error('上传文件失败:', error.message);
      throw error;
    }
  }

  /**
   * 检查文件是否存在
   */
  async checkFileExists(fileHash, fileName) {
    try {
      const axiosInstance = this.getAxiosInstance();
      const response = await axiosInstance.post('/api/files/check-exists', {
        fileHash,
        fileName
      });

      if (response.status === 401) {
        this.handleUnauthorized();
        throw new Error('认证失败，请重新登录');
      }

      return response.data.success && response.data.data.exists;
    } catch (error) {
      if (error.response && error.response.status === 401) {
        this.handleUnauthorized();
      }
      logger.error('检查文件是否存在失败:', error.message);
      return false;
    }
  }

  /**
   * 小文件上传
   */
  async uploadSmallFile(localPath, remotePath, onProgress) {
    const axiosInstance = this.getAxiosInstance();
    const formData = new FormData();
    const startTime = Date.now();
    
    formData.append('file', fs.createReadStream(localPath));
    formData.append('path', remotePath);

    try {
      const response = await axiosInstance.post('/api/upload', formData, {
        headers: formData.getHeaders(),
        timeout: 300000, // 🔥 5分钟超时
        maxContentLength: Infinity,
        maxBodyLength: Infinity,
        onUploadProgress: (progressEvent) => {
          if (onProgress && progressEvent.total) {
            // 🔥 注意：不在回调中使用 await，避免阻塞上传
            // 小文件上传速度快，流量限制在分片上传中实现即可
            
            const elapsed = (Date.now() - startTime) / 1000;
            const speed = elapsed > 0 ? progressEvent.loaded / elapsed : 0;
            onProgress({
              transferred: progressEvent.loaded,
              total: progressEvent.total,
              speed: speed
            });
          }
        }
      });

      if (response.status === 401) {
        this.handleUnauthorized();
        throw new Error('认证失败，请重新登录');
      }

      if (response.data.success) {
        logger.info('文件上传成功');
        return { skipped: false };
      } else {
        throw new Error(response.data.message || '上传失败');
      }
    } catch (error) {
      // 处理401错误
      if (error.response && error.response.status === 401) {
        this.handleUnauthorized();
      }
      // 处理EPIPE错误
      if (error.code === 'EPIPE' || error.message.includes('EPIPE')) {
        logger.error('连接断开（EPIPE），可能是网络问题或服务器超时');
        throw new Error('上传连接中断，请检查网络并重试');
      }
      throw error;
    }
  }

  /**
   * 大文件分片上传
   */
  async uploadLargeFile(localPath, remotePath, fileHash, onProgress) {
    const stats = fs.statSync(localPath);
    const totalSize = stats.size;
    const totalChunks = Math.ceil(totalSize / this.chunkSize);

    let uploadedSize = 0;
    const startTime = Date.now();

    for (let i = 0; i < totalChunks; i++) {
      const start = i * this.chunkSize;
      const end = Math.min(start + this.chunkSize, totalSize);
      const chunkBuffer = Buffer.alloc(end - start);

      // 读取分片
      const fd = fs.openSync(localPath, 'r');
      fs.readSync(fd, chunkBuffer, 0, end - start, start);
      fs.closeSync(fd);

      // 🔥 流量限制：等待令牌（带超时保护）
      const chunkSize = end - start;
      try {
        await Promise.race([
          uploadLimiter.acquire(chunkSize),
          new Promise((_, reject) => 
            setTimeout(() => reject(new Error('流量限制器超时')), 30000)
          )
        ]);
      } catch (error) {
        if (error.message === '流量限制器超时') {
          logger.warn(`分片 ${i + 1}/${totalChunks} 流量限制超时，跳过限流`);
          // 超时则跳过限流，继续上传
        } else {
          throw error;
        }
      }

      // 上传分片（带重试）
      const formData = new FormData();
      formData.append('file', chunkBuffer, {
        filename: `chunk_${i}`,
        contentType: 'application/octet-stream'
      });
      formData.append('fileHash', fileHash);
      formData.append('chunkIndex', i);
      formData.append('totalChunks', totalChunks);
      formData.append('fileName', path.basename(localPath));
      formData.append('path', remotePath); // 🔥 添加远程路径

      const axiosInstance = this.getAxiosInstance();
      
      // 重试逻辑
      let retryCount = 0;
      const maxRetries = 3;
      let lastError;
      
      while (retryCount <= maxRetries) {
        try {
          const chunkResponse = await axiosInstance.post('/api/upload/chunk', formData, {
            headers: formData.getHeaders(),
            timeout: 300000, // 🔥 5分钟超时
            maxContentLength: Infinity,
            maxBodyLength: Infinity
          });
          
          if (chunkResponse.status === 401) {
            this.handleUnauthorized();
            throw new Error('认证失败，请重新登录');
          }
          
          break; // 成功，退出重试循环
        } catch (error) {
          if (error.response && error.response.status === 401) {
            this.handleUnauthorized();
            throw error; // 401错误不重试
          }
          lastError = error;
          retryCount++;
          
          if (error.code === 'EPIPE' || error.message.includes('EPIPE') || 
              error.code === 'ECONNRESET' || error.message.includes('socket hang up')) {
            if (retryCount <= maxRetries) {
              logger.warn(`分片 ${i + 1}/${totalChunks} 上传失败，重试 ${retryCount}/${maxRetries}...`);
              await new Promise(resolve => setTimeout(resolve, 1000 * retryCount)); // 指数退避
              continue;
            }
          }
          throw error;
        }
      }
      
      if (retryCount > maxRetries && lastError) {
        throw new Error(`分片 ${i + 1} 上传失败（已重试${maxRetries}次）: ${lastError.message}`);
      }

      uploadedSize = end;
      
      if (onProgress) {
        const elapsed = (Date.now() - startTime) / 1000;
        const speed = uploadedSize / elapsed;
        onProgress({
          transferred: uploadedSize,
          total: totalSize,
          speed: speed
        });
      }
    }

    // 注意：服务器会在最后一个分片上传时自动触发合并
    // 所以这里不需要手动调用合并接口
    logger.info('所有分片上传完成，服务器会自动合并');
    return { skipped: false };
  }

  /**
   * 下载文件（支持断点续传）
   * @param {number|string} fileId - 文件ID
   * @param {string} localPath - 本地保存路径
   * @param {function} onProgress - 进度回调
   */
  async downloadFile(fileId, localPath, onProgress) {
    try {
      const axiosInstance = this.getAxiosInstance();

      // 检查本地文件是否存在
      let downloadedSize = 0;
      if (fs.existsSync(localPath)) {
        const stats = fs.statSync(localPath);
        downloadedSize = stats.size;
      }

      logger.info(`开始下载文件ID: ${fileId}, 已下载: ${downloadedSize}`);

      const startTime = Date.now();

      // 下载文件（服务器会自动处理Range请求）
      const response = await axiosInstance.get(`/api/files/${fileId}/download`, {
        responseType: 'stream',
        timeout: 300000, // 🔥 5分钟超时
        headers: downloadedSize > 0 ? {
          'Range': `bytes=${downloadedSize}-`
        } : {},
        onDownloadProgress: (progressEvent) => {
          if (onProgress) {
            const currentTransferred = downloadedSize + progressEvent.loaded;
            const elapsed = (Date.now() - startTime) / 1000;
            const speed = elapsed > 0 ? progressEvent.loaded / elapsed : 0;
            
            onProgress({
              transferred: currentTransferred,
              total: progressEvent.total ? downloadedSize + progressEvent.total : currentTransferred,
              speed: speed
            });
          }
        }
      });

      if (response.status === 401) {
        this.handleUnauthorized();
        throw new Error('认证失败，请重新登录');
      }

      // 写入文件（追加模式或新建）
      const writer = fs.createWriteStream(localPath, {
        flags: downloadedSize > 0 ? 'a' : 'w',
        start: downloadedSize
      });

      // 🔥 流量限制：使用 Transform 流实现
      const { Transform } = require('stream');
      const rateLimitTransform = new Transform({
        async transform(chunk, encoding, callback) {
          try {
            // 等待令牌（带超时保护）
            await Promise.race([
              downloadLimiter.acquire(chunk.length),
              new Promise((_, reject) => 
                setTimeout(() => reject(new Error('流量限制器超时')), 30000)
              )
            ]);
            callback(null, chunk);
          } catch (error) {
            if (error.message === '流量限制器超时') {
              // 超时则跳过限流，继续下载
              callback(null, chunk);
            } else {
              callback(error);
            }
          }
        }
      });

      response.data.pipe(rateLimitTransform).pipe(writer);

      return new Promise((resolve, reject) => {
        writer.on('finish', () => {
          logger.info('文件下载成功');
          resolve({ skipped: false });
        });
        writer.on('error', reject);
      });
    } catch (error) {
      if (error.response && error.response.status === 401) {
        this.handleUnauthorized();
      }
      logger.error('下载文件失败:', error.message);
      if (error.response) {
        logger.error('服务器响应:', error.response.status, error.response.data);
      }
      throw error;
    }
  }

  /**
   * 创建目录
   */
  async mkdir(dirPath) {
    try {
      const axiosInstance = this.getAxiosInstance();
      
      // 统一使用POSIX路径（/）
      const normalizedPath = dirPath.replace(/\\/g, '/');
      
      // 从路径中提取文件夹名称和父路径
      const folderName = path.posix.basename(normalizedPath);
      const parentPath = path.posix.dirname(normalizedPath);
      
      logger.info(`创建目录: ${normalizedPath} (名称: ${folderName}, 父路径: ${parentPath})`);
      
      const response = await axiosInstance.post('/api/files/folder', {
        name: folderName,
        parent_path: parentPath === '.' ? '/' : parentPath
      });

      if (response.status === 401) {
        this.handleUnauthorized();
        throw new Error('认证失败，请重新登录');
      }

      if (!response.data.success) {
        // 如果是"文件夹已存在"，不抛出错误
        if (response.data.message && response.data.message.includes('已存在')) {
          logger.info(`目录已存在，跳过: ${dirPath}`);
          return;
        }
        throw new Error(response.data.message || '创建目录失败');
      }

      logger.info(`目录创建成功: ${dirPath}`);
    } catch (error) {
      // 处理401错误
      if (error.response && error.response.status === 401) {
        this.handleUnauthorized();
        throw error;
      }
      // 忽略409错误（文件夹已存在）
      if (error.response && error.response.status === 400 && 
          error.response.data.message && error.response.data.message.includes('已存在')) {
        logger.info(`目录已存在，跳过: ${dirPath}`);
        return;
      }
      logger.error('创建目录失败:', error.message);
      throw error;
    }
  }

  /**
   * 删除文件
   */
  async deleteFile(fileId) {
    try {
      const axiosInstance = this.getAxiosInstance();
      const response = await axiosInstance.delete(`/api/files/${fileId}`);

      if (response.status === 401) {
        this.handleUnauthorized();
        throw new Error('认证失败，请重新登录');
      }

      if (!response.data.success) {
        throw new Error(response.data.message || '删除文件失败');
      }

      logger.info(`文件删除成功: ${fileId}`);
    } catch (error) {
      if (error.response && error.response.status === 401) {
        this.handleUnauthorized();
      }
      logger.error('删除文件失败:', error.message);
      throw error;
    }
  }

  /**
   * 计算文件MD5（完整哈希，用于精确检查）
   */
  async calculateFileMD5(filePath) {
    return new Promise((resolve, reject) => {
      const hash = require('crypto').createHash('md5');
      const stream = fs.createReadStream(filePath);
      
      stream.on('data', (data) => hash.update(data));
      stream.on('end', () => resolve(hash.digest('hex')));
      stream.on('error', reject);
    });
  }

  /**
   * 计算文件快速哈希（只读取文件头部和尾部，速度快）
   * 用于快速去重检查，降低大文件上传前的等待时间
   */
  async calculateFastHash(filePath) {
    return new Promise((resolve, reject) => {
      const stats = fs.statSync(filePath);
      const fileSize = stats.size;
      const hash = require('crypto').createHash('md5');
      
      // 如果文件小于10MB，直接计算完整哈希
      if (fileSize < 10 * 1024 * 1024) {
        return this.calculateFileMD5(filePath).then(resolve).catch(reject);
      }
      
      // 大文件：读取文件头1MB + 文件尾1MB + 文件大小和修改时间
      const sampleSize = 1024 * 1024; // 1MB
      const headBuffer = Buffer.alloc(Math.min(sampleSize, fileSize));
      const tailBuffer = Buffer.alloc(Math.min(sampleSize, fileSize));
      
      try {
        const fd = fs.openSync(filePath, 'r');
        
        // 读取文件头
        fs.readSync(fd, headBuffer, 0, headBuffer.length, 0);
        hash.update(headBuffer);
        
        // 读取文件尾
        if (fileSize > sampleSize) {
          fs.readSync(fd, tailBuffer, 0, tailBuffer.length, fileSize - sampleSize);
          hash.update(tailBuffer);
        }
        
        fs.closeSync(fd);
        
        // 加入文件大小和修改时间
        hash.update(Buffer.from(fileSize.toString()));
        hash.update(Buffer.from(stats.mtime.getTime().toString()));
        
        resolve(hash.digest('hex'));
      } catch (error) {
        reject(error);
      }
    });
  }

  /**
   * 递归上传文件夹
   * @param {string} localFolderPath - 本地文件夹路径
   * @param {string} remoteFolderPath - 远程文件夹路径
   * @param {function} onProgress - 进度回调
   * @returns {Object} 上传结果统计
   */
  async uploadFolder(localFolderPath, remoteFolderPath, onProgress) {
    const stats = {
      totalFiles: 0,
      uploadedFiles: 0,
      totalSize: 0,
      uploadedSize: 0,
      errors: []
    };

    try {
      logger.info(`开始上传文件夹: ${localFolderPath} -> ${remoteFolderPath}`);

      // 首先创建远程文件夹
      await this.mkdir(remoteFolderPath);

      // 递归扫描文件夹
      const files = await this.scanFolder(localFolderPath);
      
      // 🔥 防御性检查：确保返回的是数组
      if (!files || !Array.isArray(files)) {
        logger.error('扫描文件夹返回结果异常，不是有效的数组');
        throw new Error('扫描文件夹失败：返回结果异常');
      }
      
      stats.totalFiles = files.length;
      stats.totalSize = files.reduce((sum, f) => sum + f.size, 0);

      logger.info(`扫描到 ${files.length} 个文件，总大小: ${stats.totalSize}`);

      // 收集所有需要创建的目录
      const dirsToCreate = new Set();
      const fileUploads = [];

      for (const file of files) {
        const relativePath = path.relative(localFolderPath, file.path);
        const normalizedRelative = relativePath.replace(/\\/g, '/');
        const remoteFilePath = path.posix.join(remoteFolderPath, normalizedRelative);
        const remoteDir = path.posix.dirname(remoteFilePath);

        logger.info(`文件映射: ${relativePath} -> ${remoteFilePath}`);

        // 收集所有父目录
        let currentDir = remoteDir;
        while (currentDir && currentDir !== '/' && currentDir !== remoteFolderPath && currentDir !== '.') {
          dirsToCreate.add(currentDir);
          currentDir = path.posix.dirname(currentDir);
        }

        fileUploads.push({
          localPath: file.path,
          remotePath: remoteFilePath,
          name: file.name,
          size: file.size
        });
      }

      // 按路径深度排序并创建目录
      const sortedDirs = Array.from(dirsToCreate).sort((a, b) => {
        return a.split('/').length - b.split('/').length;
      });

      logger.info(`需要创建 ${sortedDirs.length} 个目录`);
      for (const dir of sortedDirs) {
        try {
          logger.info(`创建目录: ${dir}`);
          await this.mkdir(dir);
        } catch (error) {
          logger.error(`创建目录失败: ${dir}`, error.message);
          // 继续尝试创建其他目录
        }
      }

      // 逐个上传文件
      for (const fileInfo of fileUploads) {
        try {
          logger.info(`开始上传: ${fileInfo.name} -> ${fileInfo.remotePath}`);

          // 上传文件
          const fileProgress = (progressInfo) => {
            if (onProgress) {
              onProgress({
                currentFile: fileInfo.name,
                fileProgress: (progressInfo.transferred / progressInfo.total) * 100,
                totalProgress: ((stats.uploadedSize + progressInfo.transferred) / stats.totalSize) * 100,
                uploadedFiles: stats.uploadedFiles,
                totalFiles: stats.totalFiles
              });
            }
          };

          await this.uploadFile(fileInfo.localPath, fileInfo.remotePath, fileProgress);
          
          stats.uploadedFiles++;
          stats.uploadedSize += fileInfo.size;
          
          logger.info(`✅ 已上传 ${stats.uploadedFiles}/${stats.totalFiles}: ${fileInfo.name}`);
        } catch (error) {
          logger.error(`❌ 上传文件失败: ${fileInfo.name}`, error);
          stats.errors.push({ file: fileInfo.name, error: error.message });
        }
      }

      logger.info(`文件夹上传完成: ${stats.uploadedFiles}/${stats.totalFiles} 成功`);
      return stats;
    } catch (error) {
      logger.error('上传文件夹失败:', error);
      throw error;
    }
  }

  /**
   * 递归扫描文件夹，获取所有文件
   * @param {string} dirPath - 文件夹路径
   * @returns {Array} 文件列表
   */
  async scanFolder(dirPath) {
    const files = [];

    try {
      const scan = async (currentPath) => {
        try {
          const entries = await fsPromises.readdir(currentPath, { withFileTypes: true });

          for (const entry of entries) {
            const fullPath = path.join(currentPath, entry.name);

            if (entry.isDirectory()) {
              // 递归扫描子目录
              try {
                await scan(fullPath);
              } catch (error) {
                logger.warn(`扫描子目录失败: ${fullPath} - ${error.message}`);
              }
            } else if (entry.isFile()) {
              try {
                const stats = await fsPromises.stat(fullPath);
                files.push({
                  path: fullPath,
                  name: entry.name,
                  size: stats.size
                });
              } catch (error) {
                logger.warn(`读取文件信息失败: ${fullPath} - ${error.message}`);
              }
            }
          }
        } catch (error) {
          logger.error(`读取目录失败: ${currentPath} - ${error.message}`);
          throw error;
        }
      };

      await scan(dirPath);
      return files;
    } catch (error) {
      logger.error(`扫描文件夹失败: ${dirPath} - ${error.message}`);
      // 🔥 确保始终返回数组，即使出错
      return [];
    }
  }

  /**
   * 检查连接状态
   */
  async isConnected() {
    try {
      const axiosInstance = this.getAxiosInstance();
      const response = await axiosInstance.get('/health');
      
      if (response.status === 401) {
        this.handleUnauthorized();
        return false;
      }
      
      return response.data.success;
    } catch (error) {
      if (error.response && error.response.status === 401) {
        this.handleUnauthorized();
      }
      return false;
    }
  }
}

module.exports = HTTPClient;
